home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Aminet 40
/
Aminet 40 (2000)(Schatztruhe)[!][Dec 2000].iso
/
Aminet
/
misc
/
emu
/
msh-156.lha
/
han
/
hanfile.c
< prev
next >
Wrap
C/C++ Source or Header
|
1996-12-22
|
25KB
|
1,032 lines
/*-
* $Id: hanfile.c,v 1.56 1996/12/22 00:22:33 Rhialto Rel $
* $Log: hanfile.c,v $
* Revision 1.56 1996/12/22 00:22:33 Rhialto
* Also update file lock (dir entry) on filw write error.
* And file write success.
*
* Revision 1.55 1993/12/30 23:28:00 Rhialto
* Freeze for MAXON5.
* Check Write() size to see it it will all fit.
* Optionally, update time of file on creation only.
* Lots of small changes for LONGNAMES option.
*
* Revision 1.54 1993/06/24 05:12:49 Rhialto
* MSCreateDir gave you an exclusive lock contrary to our liberal policy.
* DICE 2.07.54R.
*
* Revision 1.53 92/10/25 02:29:23 Rhialto
* Default conversion settable.
* OFFSET_END seeks were done in the wrong direction ARGH!
* Protect seek pos from being past EOF (due to SetFileSize).
* Count free clusters instead of free sectors.
*
* Revision 1.51 92/04/17 15:37:53 Rhialto
* Freeze for MAXON.
*
* Revision 1.46 91/10/06 18:24:36 Rhialto
*
* Freeze for MAXON
*
* Revision 1.45 91/10/03 23:36:16 Rhialto
* Implement in-situ conversions during Read()/Write()
*
* Revision 1.43 91/09/28 01:45:39 Rhialto
* Changed to newer syslog stuff.
*
* Revision 1.42 91/06/13 23:56:14 Rhialto
* DICE conversion
*
* Revision 1.40 91/03/03 17:53:53 Rhialto
* Freeze for MAXON
*
* Revision 1.32 90/11/23 23:54:57 Rhialto
* Prepare for syslog
*
* Revision 1.31 90/11/10 02:50:04 Rhialto
* Patch 3a. Update modification time of directories.
*
* Revision 1.30 90/06/04 23:17:33 Rhialto
* Release 1 Patch 3
*
* HANFILE.C
*
* The code for the messydos file system handler.
*
* This parts handles files and the File Allocation Table.
*
* This code is (C) Copyright 1989-1993 by Olaf Seibert. All rights reserved.
* May not be used or copied without a licence.
-*/
#include "han.h"
#include "dos.h"
#include <string.h>
#if CONVERSIONS
# include "hanconv.h"
#endif
#if HDEBUG
# include "syslog.h"
#else
# define debug(x)
#endif
Prototype int GetFat(void);
Prototype void FreeFat(void);
Prototype word GetFatEntry(word cluster);
Prototype void SetFatEntry(word cluster, word value);
Prototype word FindFreeCluster(word prev);
Prototype word ExtendClusterChain(word cluster);
Prototype void FreeClusterChain(word cluster);
Prototype struct MSFileHandle *MakeMSFileHandle(struct MSFileLock *fl, long mode);
Prototype struct MSFileHandle *MSOpen(struct MSFileLock *parentdir, char *name, long mode);
Prototype long MSClose(struct MSFileHandle *fh);
Prototype long FilePos(struct MSFileHandle *fh, long position, long mode);
Prototype void AdjustSeekPos(struct MSFileHandle *fh);
Prototype long MSSeek(struct MSFileHandle *fh, long position, long mode);
Prototype long MSRead(struct MSFileHandle *fh, byte *userbuffer, long size);
Prototype long MSWrite(struct MSFileHandle *fh, byte *userbuffer, long size);
Prototype long MSDeleteFile(struct MSFileLock *parentdir, byte *name);
Prototype long MSSetDate(struct MSFileLock *parentdir, byte *name, struct DateStamp *datestamp);
Prototype struct MSFileLock *MSCreateDir(struct MSFileLock *parentdir, byte *name);
Prototype long MSRename(struct MSFileLock *slock, byte *sname, struct MSFileLock *dlock, byte *dname);
/*
* Read the FAT from the disk, and count the free clusters.
*/
int
GetFat()
{
int i;
byte *secptr;
if (!Fat && !(Fat = AllocMem((long) Disk.bps * Disk.spf, BufMemType))) {
debug(("No memory for FAT\n"));
return 0;
}
FatDirty = FALSE;
for (i = 0; i < Disk.spf; i++) {
if (secptr = ReadSec(Disk.res + i)) {
CopyMem(secptr, Fat + i * Disk.bps, (long) Disk.bps);
FreeSec(secptr);
} else {
/* q&d way to set the entire FAT to FAT_EOF */
setmem(Fat + i * Disk.bps, (int) Disk.bps, FAT_EOF); /* 0xFF */
}
}
debug(("counting free clusters\n"));
Disk.freeclusts = 0;
for (i = MS_FIRSTCLUST; i <= Disk.maxclst; i++) {
if (GetFatEntry((word) i) == FAT_UNUSED)
Disk.freeclusts++;
}
return 1;
}
void
FreeFat()
{
debug(("FreeFat()\n"));
if (Fat) {
FreeMem(Fat, (long) Disk.bps * Disk.spf);
Fat = NULL;
FatDirty = FALSE;
}
}
/*-
* The FAT consists of 12-bits entries for each cluster,
* indicating the next cluster in the chain, or FFF for EOF.
*
* Every two entries are packed in three bytes, like this:
*
* Two entries abc 123 (for one cluster and the next)
* are packed as bc 3a 12
-*/
word
GetFatEntry(cluster)
word cluster;
{
if (!Fat && !GetFat())
return FAT_EOF;
if (Disk.fat16bits) {
return OtherEndianWord(((word *)Fat)[cluster]);
} else {
int offset = 3 * (cluster / 2);
word twoentries;
if (cluster & 1) {
twoentries = Fat[offset + 1] >> 4;
twoentries |= Fat[offset + 2] << 4;
} else {
twoentries = Fat[offset];
twoentries |= (Fat[offset + 1] & 0x0F) << 8;
}
/*
* Convert the special values 0xFF7 .. 0xFFF to 16 bits so they
* can be checked consistently.
*/
if (twoentries >= 0xFF7)
twoentries |= 0xF000;
return twoentries;
}
}
#if ! READONLY
void
SetFatEntry(cluster, value)
word cluster;
word value;
{
if (!Fat && !GetFat())
return;
if (Disk.fat16bits) {
((word *)Fat)[cluster] = OtherEndianWord(value);
} else {
int offset = 3 * (cluster / 2);
if (cluster & 1) { /* 123 kind of entry */
Fat[offset + 2] = value >> 4;
Fat[offset + 1] &= 0x0F;
Fat[offset + 1] |= (value & 0x0F) << 4;
} else { /* abc kind of entry */
Fat[offset + 0] = value;
Fat[offset + 1] &= 0xF0;
Fat[offset + 1] |= (value >> 8) & 0x0F;
}
}
FatDirty = TRUE;
StartTimer(4);
}
/*
* Find a free cluster to install as the one following this one. Start
* looking for it right after the given one, so we allocate the cluster
* chain as contiguous as possible. If we run off the end of the disk, we
* start again at the beginning. The termination test should not be
* necessary (and won't work if we are given MSFIRSTCLUST - 1) but won't
* harm either.
*/
word
FindFreeCluster(prev)
word prev;
{
word i;
if (prev == 0 || prev == FAT_EOF)
prev = MS_FIRSTCLUST - 1;
if (Disk.freeclusts > 0) {
for (i = prev + 1; i != prev; i++) {
if (i > Disk.maxclst) /* Wrap around */
i = MS_FIRSTCLUST;
if (GetFatEntry(i) == FAT_UNUSED) {
SetFatEntry(i, FAT_EOF);
if (prev >= MS_FIRSTCLUST)
SetFatEntry(prev, i);
Disk.freeclusts--;
return i;
}
}
}
return FAT_EOF;
}
/*
* Add a cluster to a cluster chain. For input, we get some cluster we
* know that is on the chain, even if it is the first one.
*/
word
ExtendClusterChain(cluster)
word cluster;
{
word nextcluster;
/*
* Find the end of the cluster chain to tack the new cluster on to.
* Then FindFreeCluster will (or won't) extend the chain for us.
*/
if (cluster != 0) {
while ((nextcluster = NextCluster(cluster)) != FAT_EOF) {
cluster = nextcluster;
}
}
return FindFreeCluster(cluster);
}
/*
* Free a chain of clusters by setting their FAT entries to FAT_UNUSED.
*/
void
FreeClusterChain(cluster)
word cluster;
{
word nextcluster;
while (cluster != FAT_EOF) {
nextcluster = NextCluster(cluster);
SetFatEntry(cluster, FAT_UNUSED);
Disk.freeclusts++;
cluster = nextcluster;
}
}
#endif /* READONLY */
struct MSFileHandle *
MakeMSFileHandle(fl, mode)
struct MSFileLock *fl;
long mode;
{
struct MSFileHandle *fh;
if (fl->msfl_Msd.msd_Attributes & ATTR_DIR) {
error = ERROR_OBJECT_WRONG_TYPE;
fh = NULL;
} else if (fh = AllocMem((long) sizeof (*fh), MEMF_PUBLIC)) {
#if ! READONLY
/* Do we need to truncate the file? */
if ((mode == MODE_NEWFILE) && fl->msfl_Msd.msd_Cluster) {
FreeClusterChain(fl->msfl_Msd.msd_Cluster);
fl->msfl_Msd.msd_Cluster = 0;
fl->msfl_Msd.msd_Filesize = 0;
UpdateFileLock(fl);
}
#endif
fh->msfh_Cluster = fl->msfl_Msd.msd_Cluster;
fh->msfh_SeekPos = 0;
fh->msfh_FileLock = fl;
#if CONVERSIONS
fh->msfh_Conversion = ConvNone;
#endif
} else {
error = ERROR_NO_FREE_STORE;
}
return fh;
}
/*
* This routine opens a file.
*/
struct MSFileHandle *
MSOpen(parentdir, name, mode)
struct MSFileLock *parentdir;
char *name;
long mode;
{
struct MSFileLock *fl;
struct MSFileHandle *fh = NULL;
long lockmode;
switch (mode) {
case MODE_NEWFILE:
case MODE_READWRITE:
lockmode = EXCLUSIVE_LOCK ^ MODE_CREATEFILE;
break;
default:
mode = MODE_OLDFILE;
case MODE_OLDFILE:
lockmode = SHARED_LOCK;
}
#if CONVERSIONS
ConversionImbeddedInFileName = DefaultConversion;
#endif
if (fl = MSLock(parentdir, name, lockmode)) {
makefh:
fh = MakeMSFileHandle(fl, mode);
if (fh) {
#if CONVERSIONS
fh->msfh_Conversion = ConversionImbeddedInFileName;
#endif
} else {
MSUnLock(fl);
}
return fh;
}
#if ! READONLY
/*
* If the file was not found, see if we can make a new one. Therefore
* we need to have an empty spot in the desired directory, and create
* an MSFileLock for it.
*/
if (!(lockmode & MODE_CREATEFILE) && (fl = EmptyFileLock)) {
debug(("Creating new file\n"));
EmptyFileLock = NULL;
fl->msfl_Msd.msd_Attributes = ATTR_ARCHIVED;
UpdateFileLock(fl);
#if ! CREATIONDATE_ONLY
UpdateFileLock(fl->msfl_Parent);
#endif
goto makefh;
}
if (EmptyFileLock) {
MSUnLock(EmptyFileLock);
EmptyFileLock = NULL;
}
#endif
return NULL;
}
long
MSClose(fh)
struct MSFileHandle *fh;
{
if (fh) {
MSUnLock(fh->msfh_FileLock);
FreeMem(fh, (long) sizeof (*fh));
}
return DOSTRUE;
}
long
FilePos(fh, position, mode)
struct MSFileHandle *fh;
long position;
long mode;
{
switch (mode) {
default:
case OFFSET_BEGINNING:
return position;
case OFFSET_CURRENT:
return fh->msfh_SeekPos + position;
break;
case OFFSET_END:
return fh->msfh_FileLock->msfl_Msd.msd_Filesize + position;
}
}
#if defined(ACTION_SET_FILE_SIZE)
void
AdjustSeekPos(fh)
struct MSFileHandle *fh;
{
if (fh->msfh_SeekPos > fh->msfh_FileLock->msfl_Msd.msd_Filesize) {
/* fh->msfh_Cluster needs to be fully recalculated */
MSSeek(fh, 0, OFFSET_BEGINNING);
MSSeek(fh, 0, OFFSET_END);
}
}
#endif
long
MSSeek(fh, position, mode)
struct MSFileHandle *fh;
long position;
long mode;
{
long oldpos;
long newpos;
long filesize = fh->msfh_FileLock->msfl_Msd.msd_Filesize;
word cluster = fh->msfh_Cluster;
word oldcluster;
word newcluster;
#if defined(ACTION_SET_FILE_SIZE)
if (fh->msfh_SeekPos > fh->msfh_FileLock->msfl_Msd.msd_Filesize) {
fh->msfh_SeekPos = fh->msfh_FileLock->msfl_Msd.msd_Filesize;
}
#endif
oldpos = fh->msfh_SeekPos;
newpos = FilePos(fh, position, mode);
if (newpos < 0 || newpos > filesize) {
error = ERROR_SEEK_ERROR;
return -1;
}
newcluster = newpos / Disk.bpc;
oldcluster = oldpos / Disk.bpc;
debug(("MSSeek: pos %ld mode %ld oldcluster %ld newcluster %ld\n",
position, mode, oldcluster, newcluster));
if (oldcluster > newcluster) { /* Seek backwards */
cluster = fh->msfh_FileLock->msfl_Msd.msd_Cluster;
oldcluster = 0;
debug(("rewind: cluster %ld\n", cluster));
}
if (oldcluster < newcluster) {
if (CheckLock(fh->msfh_FileLock))
return -1;
while (oldcluster < newcluster) {
cluster = NextCluster(cluster);
oldcluster++;
debug(("forward: %ldth cluster %ld\n", oldcluster, cluster));
}
}
fh->msfh_Cluster = cluster;
fh->msfh_SeekPos = newpos;
debug(("MSSeek: newpos %ld cluster %ld\n", newpos, cluster));
return oldpos;
}
long
MSRead(fh, userbuffer, size)
struct MSFileHandle *fh;
byte *userbuffer;
long size;
{
long oldsize;
if (CheckLock(fh->msfh_FileLock))
return -1;
#if defined(ACTION_SET_FILE_SIZE)
AdjustSeekPos(fh);
#endif
if (fh->msfh_SeekPos + size > fh->msfh_FileLock->msfl_Msd.msd_Filesize)
size = fh->msfh_FileLock->msfl_Msd.msd_Filesize - fh->msfh_SeekPos;
oldsize = size;
while (size > 0) {
word offset;
word sector;
byte *diskbuffer;
long tocopy;
offset = fh->msfh_SeekPos % Disk.bpc;
sector = ClusterOffsetToSector(fh->msfh_Cluster, (word) offset);
if (diskbuffer = ReadSec(sector)) {
offset %= Disk.bps;
tocopy = lmin(size, Disk.bps - offset);
#if CONVERSIONS
(rd_Conv[fh->msfh_Conversion])(diskbuffer + offset, userbuffer,
tocopy);
#else
CopyMem(diskbuffer + offset, userbuffer, tocopy);
#endif
userbuffer += tocopy;
size -= tocopy;
FreeSec(diskbuffer);
/* MSSeek(fh, tocopy, (long) OFFSET_CURRENT); */
fh->msfh_SeekPos += tocopy;
if (fh->msfh_SeekPos % Disk.bpc == 0)
fh->msfh_Cluster = NextCluster(fh->msfh_Cluster);
} else { /* Read error. Return amount successfully
* read, if any. Else return -1 for error. */
if (size == oldsize) {
return -1;
}
return oldsize - size;
}
}
debug(("MSRead: SeekPos %ld\n", fh->msfh_SeekPos));
return oldsize;
}
long
MSWrite(fh, userbuffer, size)
struct MSFileHandle *fh;
byte *userbuffer;
long size;
{
#if READONLY
return -1;
#else
long oldsize;
struct MSFileLock *fl = fh->msfh_FileLock;
word prevclust = fl->msfl_Msd.msd_Cluster;
word update = 0;
if (CheckLock(fl))
return -1;
if (fl->msfl_Msd.msd_Attributes & ATTR_READONLY) {
error = ERROR_WRITE_PROTECTED;
return -1;
}
#if defined(ACTION_SET_FILE_SIZE)
AdjustSeekPos(fh);
#endif
oldsize = size;
/* Check if this will fit on the disk */
{
long new;
long old;
new = (fh->msfh_SeekPos + size + Disk.bpc - 1) / Disk.bpc;
old = (fl->msfl_Msd.msd_Filesize + Disk.bpc - 1) / Disk.bpc;
if (new - old > (long)Disk.freeclusts) {
error = ERROR_DISK_FULL;
goto some_error;
}
}
while (size > 0) {
/*
* Do we need to extend the file?
*/
if (fh->msfh_Cluster == 0 || fh->msfh_Cluster == FAT_EOF) {
word newclust;
newclust = ExtendClusterChain(prevclust);
debug(("Extend with %ld\n", (long)newclust));
if (newclust != FAT_EOF) {
if (prevclust == 0) { /* Record first cluster in dir */
fl->msfl_Msd.msd_Cluster = newclust;
}
fh->msfh_Cluster = newclust;
prevclust = newclust;
} else {
/* "Can't happen" */
error = ERROR_DISK_FULL;
goto some_error;
}
}
{
word offset;
word sector;
byte *diskbuffer;
long tocopy;
offset = fh->msfh_SeekPos % Disk.bpc;
sector = ClusterOffsetToSector(fh->msfh_Cluster, (word) offset);
offset %= Disk.bps;
tocopy = lmin(size, Disk.bps - offset);
if (offset == 0 && fh->msfh_SeekPos >= fl->msfl_Msd.msd_Filesize)
diskbuffer = EmptySec(sector);
else
diskbuffer = ReadSec(sector);
if (diskbuffer != NULL) {
#if CONVERSIONS
(wr_Conv[fh->msfh_Conversion])(userbuffer, diskbuffer + offset, tocopy);
#else
CopyMem(userbuffer, diskbuffer + offset, tocopy);
#endif
userbuffer += tocopy;
size -= tocopy;
MarkSecDirty(diskbuffer);
FreeSec(diskbuffer);
/* MSSeek(fh, tocopy, (long) OFFSET_CURRENT); */
fh->msfh_SeekPos += tocopy;
if (fh->msfh_SeekPos % Disk.bpc == 0)
fh->msfh_Cluster = NextCluster(fh->msfh_Cluster);
if (fh->msfh_SeekPos > fl->msfl_Msd.msd_Filesize)
fl->msfl_Msd.msd_Filesize = fh->msfh_SeekPos;
fl->msfl_Msd.msd_Attributes |= ATTR_ARCHIVED;
update = 1;
} else { /* Write error. */
some_error:
if (update) {
#if CREATIONDATE_ONLY
DirtyFileLock(fl);
#else
UpdateFileLock(fl);
#endif
}
#if 1
return -1; /* We lose the information about how much
* data we wrote, but the standard file system
* seems to do it this way. */
#else
if (size == oldsize) {
return -1;
}
return oldsize - size; /* Amount successfully written */
#endif
}
}
}
if (update) {
#if CREATIONDATE_ONLY
DirtyFileLock(fl);
#else
UpdateFileLock(fl);
#endif
}
debug(("MSWrite: SeekPos %ld\n", fh->msfh_SeekPos));
return oldsize;
#endif
}
long
MSDeleteFile(parentdir, name)
struct MSFileLock *parentdir;
byte *name;
{
#if READONLY
return DOSFALSE;
#else
struct MSFileLock *fl;
fl = MSLock(parentdir, name, EXCLUSIVE_LOCK);
if (fl) {
if (fl->msfl_Msd.msd_Attributes & ATTR_READONLY) {
error = ERROR_DELETE_PROTECTED;
goto some_error;
}
if (fl->msfl_Msd.msd_Attributes & ATTR_DIRECTORY) {
struct FileInfoBlock fib;
/*
* We normally can't get REAL exclusive locks on directories,
* so we check here just to be sure. We don't want to delete
* anyone's current directory, do we?
*/
if (fl->msfl_Refcount != 1 || fl == RootLock) {
error = ERROR_OBJECT_IN_USE;
goto some_error;
}
if (MSExamine(fl, &fib) && /* directory itself */
MSExNext(fl, &fib)) { /* should fail */
if (error == 0) {
not_empty:
error = ERROR_DIRECTORY_NOT_EMPTY;
some_error:
MSUnLock(fl);
return DOSFALSE;
}
}
if (error != ERROR_NO_MORE_ENTRIES)
goto some_error;
error = 0;
}
if (fl->msfl_Msd.msd_Cluster)
FreeClusterChain(fl->msfl_Msd.msd_Cluster);
fl->msfl_Msd.msd_Name[0] = DIR_DELETED;
WriteFileLock(fl);
#if ! CREATIONDATE_ONLY
UpdateFileLock(fl->msfl_Parent);
#endif
MSUnLock(fl);
return DOSTRUE;
}
return DOSFALSE;
#endif
}
long
MSSetDate(parentdir, name, datestamp)
struct MSFileLock *parentdir;
byte *name;
struct DateStamp *datestamp;
{
#if READONLY
return DOSFALSE;
#else
struct MSFileLock *fl;
fl = MSLock(parentdir, name, EXCLUSIVE_LOCK);
if (fl) {
ToMSDate(&fl->msfl_Msd.msd_Date, &fl->msfl_Msd.msd_Time, datestamp);
WriteFileLock(fl);
MSUnLock(fl);
return DOSTRUE;
}
return DOSFALSE;
#endif
}
/*
* Create a new directory, with its own initial "." and ".." entries.
*/
struct MSFileLock *
MSCreateDir(parentdir, name)
struct MSFileLock *parentdir;
byte *name;
{
#if READONLY
return DOSFALSE;
#else
struct MSFileLock *fl;
/*
* Go create a new file. If we fail later, we have an empty file that
* we delete again.
*/
fl = MSLock(parentdir, name, EXCLUSIVE_LOCK ^ MODE_CREATEFILE);
if (fl || error == ERROR_OBJECT_IN_USE) {
error = ERROR_OBJECT_EXISTS;
goto some_error;
}
if (error != 0) {
goto some_error;
}
if (fl = EmptyFileLock) {
debug(("Creating new dir\n"));
EmptyFileLock = NULL;
if ((fl->msfl_Msd.msd_Cluster = FindFreeCluster(FAT_EOF)) != FAT_EOF) {
struct MsDirEntry direntry;
byte *sec;
word sector;
sector = ClusterToSector(fl->msfl_Msd.msd_Cluster);
sec = EmptySec(sector);
if (sec == NULL)
goto error_no_free_store;
setmem(sec, (int) Disk.bps, 0);
/*
* Turn the file into a directory.
*/
fl->msfl_Msd.msd_Attributes = ATTR_DIRECTORY | ATTR_ARCHIVED;
fl->msfl_Refcount = 1; /* Make it non-exclusive */
UpdateFileLock(fl);
/*
* Create the "." entry.
*/
direntry = fl->msfl_Msd;
strncpy(direntry.msd_Name, DotDot + 1, L_8 + L_3);
OtherEndianMsd(&direntry);
((struct MsDirEntry *) sec)[0] = direntry;
/*
* Get the real parent directory because we will duplicate the
* directory entry in the subdirectory.
*/
parentdir = MSParentDir(fl);
if (parentdir == NULL) /* Cannot happen */
parentdir = MSDupLock(RootLock);
#if ! CREATIONDATE_ONLY
UpdateFileLock(parentdir);
#endif
/*
* Create the ".." entry.
*/
direntry = parentdir->msfl_Msd;
strncpy(direntry.msd_Name, DotDot, L_8 + L_3);
direntry.msd_Attributes = ATTR_DIRECTORY | ATTR_ARCHIVED;
OtherEndianMsd(&direntry);
((struct MsDirEntry *) sec)[1] = direntry;
MSUnLock(parentdir);
MarkSecDirty(sec);
FreeSec(sec);
/*
* Clear out the rest of the newly created directory.
*/
while ((sector = NextClusteredSector(sector)) != SEC_EOF) {
sec = EmptySec(sector);
if (sec == NULL)
goto error_no_free_store;
setmem(sec, (int) Disk.bps, 0);
MarkSecDirty(sec);
FreeSec(sec);
}
} else {
MSUnLock(fl);
fl = NULL;
MSDeleteFile(parentdir, name);
error = ERROR_DISK_FULL;
}
}
if (EmptyFileLock) {
MSUnLock(EmptyFileLock);
EmptyFileLock = NULL;
}
return fl;
error_no_free_store:
error = ERROR_NO_FREE_STORE;
some_error:
if (fl)
MSUnLock(fl);
return DOSFALSE;
#endif
}
/*
* Rename a file or directory, possibly moving it to a different
* directory.
*
* "Tuned" to also work in full directories by first deleting the source
* name, then look for a slot to put the destination name. If that fails,
* we undo the deletion. By playing with the cache, we even avoid a write
* of the sector with the undeleted entry.
*/
long
MSRename(slock, sname, dlock, dname)
struct MSFileLock *slock;
byte *sname;
struct MSFileLock *dlock;
byte *dname;
{
#if READONLY
return DOSFALSE;
#else
struct MSFileLock *sfl;
struct MSFileLock *dfl;
long success;
struct CacheSec *scache;
ulong oldstatus;
success = DOSFALSE;
scache = NULL;
dfl = NULL;
sfl = MSLock(slock, sname, SHARED_LOCK);
if (sfl == NULL || sfl == RootLock)
goto some_error;
/*
* Now we are going to pull a dirty trick with the cache. We are going
* to temporarily delete the source file, in the chache only, and
* undelete it again if we cannot create the new name. And above all
* we want to avoid unnecessary writes if we decide not to do the
* deletion after all.
*/
{
byte *sec;
byte old;
if ((sec = ReadSec(sfl->msfl_DirSector)) == NULL)
goto some_error;
scache = FindSecByBuffer(sec);
oldstatus = scache->sec_Refcount;
old = sfl->msfl_Msd.msd_Name[0];
sfl->msfl_Msd.msd_Name[0] = DIR_DELETED;
WriteFileLock(sfl);
sfl->msfl_Msd.msd_Name[0] = old;
/*
* Don't FreeSec it yet; we don't want it written out to disk.
*/
}
/*
* Now we have freed the directory entry of the source name, we might
* be able to use it for the destination name. But only if we also
* temporarily hide the MSFileLock on that spot. Gross hack ahead!
*/
sfl->msfl_DirOffset = ~sfl->msfl_DirOffset;
dfl = MSLock(dlock, dname, EXCLUSIVE_LOCK ^ MODE_CREATEFILE);
sfl->msfl_DirOffset = ~sfl->msfl_DirOffset;
if (dfl != NULL || error == ERROR_OBJECT_IN_USE) {
error = ERROR_OBJECT_EXISTS;
goto undelete;
}
dfl = EmptyFileLock;
EmptyFileLock = NULL;
if (dfl == NULL) {
/*
* Sigh, we could not create the new name. But because of that, we
* are sure that we need to write nothing to the disk at all. So
* we can safely reset the sector-dirty flag to what it was
* before, if we also restore the cached sector.
*/
undelete:
WriteFileLock(sfl);
scache->sec_Refcount = oldstatus;
goto some_error;
}
/*
* Now, if the moved entry was a directory, and it was moved to a
* different directory, we need to adapt its "..", which is the second
* entry.
*/
if (sfl->msfl_Msd.msd_Attributes & ATTR_DIRECTORY &&
sfl->msfl_Parent != dfl->msfl_Parent) {
struct MSFileLock *parentdir;
struct MsDirEntry *dir;
if (dir = (struct MsDirEntry *)
ReadSec(DirClusterToSector(sfl->msfl_Msd.msd_Cluster))) {
parentdir = MSParentDir(dfl);
/*
* Copy everything except the name which must remain "..". But
* first a quick consistency check...
*/
debug(("Creating new \"..\" "));
if (dir[1].msd_Name[1] == '.') {
CopyMem(&parentdir->msfl_Msd.msd_Attributes,
&dir[1].msd_Attributes,
(long) sizeof (struct MsDirEntry) -
OFFSETOF(MsDirEntry, msd_Attributes));
dir[1].msd_Attributes = ATTR_DIRECTORY;
OtherEndianMsd(&dir[1]);
MarkSecDirty((byte *)dir);
}
#if HDEBUG
else
debug(("!!! No \"..\" found ??\n"));
#endif
MSUnLock(parentdir);
FreeSec((byte *)dir);
}
}
/*
* Move the name from the new entry to the old filelock. We do this
* for the case that somebody else has a lock on the (possibly moved)
* file/directory. Also move the other administration.
*/
strncpy(sfl->msfl_Msd.msd_Name, dfl->msfl_Msd.msd_Name, L_8 + L_3);
sfl->msfl_DirSector = dfl->msfl_DirSector;
sfl->msfl_DirOffset = dfl->msfl_DirOffset;
/*
* Free the old, and get the new parent directory. They might be the
* same, of course...
*/
MSUnLock(sfl->msfl_Parent);
sfl->msfl_Parent = dfl->msfl_Parent;
dfl->msfl_Parent = NULL;
sfl->msfl_Msd.msd_Attributes |= ATTR_ARCHIVED;
WriteFileLock(sfl); /* Write the new name; the old name
* already has been deleted. */
#if ! CREATIONDATE_ONLY
UpdateFileLock(sfl->msfl_Parent);
UpdateFileLock(dfl->msfl_Parent);
#endif
success = DOSTRUE;
some_error:
if (sfl)
MSUnLock(sfl);
if (dfl)
MSUnLock(dfl);
if (scache)
FreeSec(scache->sec_Data);
return success;
#endif
}